﻿using OnlineEducation.Data;
using OnlineEducation.Data.Entities;
using OnlineEducation.Extensions;
using OnlineEducation.ExternalService;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace OnlineEducation.Controllers.Api
{
    /// <summary>
    /// 用户登录 Web API
    /// </summary>
    [Area("api")]
    [Produces("application/json")]
    [Route("api/[controller]/[action]")]
    public class AccountController : Controller
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly IEmailSender _emailSender;
        private readonly ILogger _logger;
        private readonly IConfiguration _configuration;

        private readonly ApplicationDbContext _context;
        private readonly IDistributedCache _distributedCache;
        private readonly IWebHostEnvironment _hostingEnvironment;

        private readonly string IP_KEY = "__ip__";
        private readonly string REG_KEY = "__register__";
        private readonly string RESET_PWD = "__resetpassword__";
        public AccountController(
            UserManager<ApplicationUser> userManager,
            SignInManager<ApplicationUser> signInManager,
            ILogger<AccountController> logger,
            IDistributedCache distributedCache,
            IEmailSender emailSender,
            IWebHostEnvironment hostingEnvironment,
            ApplicationDbContext context,
            IConfiguration configuration)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
            _distributedCache = distributedCache;
            _hostingEnvironment = hostingEnvironment;
            _context = context;
            _emailSender = emailSender;
            _configuration = configuration;

        }

        /// <summary>
        /// 发送注册验证码。
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> SendRegValidationCode(string data)
        {
            var ip = Request.HttpContext?.Connection?.RemoteIpAddress?.ToString();

            if (ip == null) return Json(new { success = false, message = "invalid ip." });

            var ipkey = IP_KEY + ip;

            var lastSendTime = _distributedCache.Get<DateTime>(ipkey);

            var timeSpan = DateTime.Now.Subtract(lastSendTime);

            _distributedCache.Set<DateTime>(ipkey, DateTime.Now);

            if (timeSpan.TotalSeconds < 50) ///如果少于50秒，阻止用户再次发送验证码，防止恶意调用。
            {
                return Json(new { success = false, message = "invalid ip." });//confuse sender.
            }

            var randomCode = GetID();

            string regkey = REG_KEY + data;

            _distributedCache.SetString(regkey, randomCode.ToString(), new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(5)));

            await _emailSender.SendEmailRegister(data, data, randomCode.ToString(), _hostingEnvironment);

            _logger.LogInformation("{$data}注册验证码已发送。");

            return Json(new { success = true, message = "验证码已发送。" });

        }


        /// <summary>
        /// 发送密码重置验证码。
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> SendResetPasswordValidationCode(string data)
        {

            var user = _context.ApplicationUsers.Where(n => n.UserName == data).FirstOrDefault();

            if (user == null) return Json(new { success = false, message = "您尚未注册平台账号。" });

            var ip = Request.HttpContext?.Connection?.RemoteIpAddress?.ToString();

            if (ip == null) return Json(new { success = false, message = "invalid ip." });

            var ipkey = IP_KEY + ip;

            var lastSendTime = _distributedCache.Get<DateTime>(ipkey);

            var timeSpan = DateTime.Now.Subtract(lastSendTime);

            _distributedCache.Set<DateTime>(ipkey, DateTime.Now);

            if (timeSpan.TotalSeconds < 50) ///如果少于50秒，阻止用户再次发送验证码，防止恶意调用。
            {
                return Json(new { success = false, message = "invalid ip." });//confuse sender.
            }

            var randomCode = GetID();

            string regkey = RESET_PWD + data;

            _distributedCache.SetString(regkey, randomCode.ToString(), new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(5)));

            await _emailSender.SendEmailResetPassword(data, data, randomCode.ToString(), _hostingEnvironment);

            _logger.LogInformation("{$data}重设密码验证码已发送。");

            return Json(new { success = true, message = "验证码已发送。" });

        }


        /// <summary>
        /// Jwt登录验证，当APP登录，调用此API，会返回一个Authorization token，保存此token 在 APP端，每次调用web api 时，放token到header，格式：Authorization: {token}。
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> Login(string mobile, string password)
        {

            if (!string.IsNullOrEmpty(mobile) && !string.IsNullOrEmpty(password))
            {
                var user = await _userManager.FindByNameAsync(mobile);

                if (user != null)
                {

                    var result = await _signInManager.CheckPasswordSignInAsync(user, password, lockoutOnFailure: false);

                    if (!result.Succeeded)
                    {
                        return Unauthorized();
                    }
                    var roles = await _userManager.GetRolesAsync(user);

                    //put some informations into auth code.
                    var claims = new List<Claim>
                    {   new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                        new Claim(ClaimTypes.Name, user.UserName),
                        new Claim("AspNet.Identity.SecurityStamp",user.SecurityStamp),
                        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                    };
                    //put user role into auth code
                    foreach (var role in roles)
                    {
                        claims.Add(new Claim(ClaimTypes.Role, role));
                    }

                    var token = new JwtSecurityToken
                    (
                        issuer: _configuration["Token:Issuer"],
                        audience: _configuration["Token:Audience"],
                        claims: claims,
                        expires: DateTime.UtcNow.AddDays(60),
                        notBefore: DateTime.UtcNow,
                        signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Token:Key"])),
                                SecurityAlgorithms.HmacSha256)
                    );

                    return Ok(new { token = "Bearer " + new JwtSecurityTokenHandler().WriteToken(token) });
                }
            }

            return BadRequest();

        }


        /// <summary>
        /// 获取一个随机的验证码
        /// </summary>
        /// <returns></returns>
        private int GetID()
        {
            Random rd = new Random();
            int num = rd.Next(100000, 999999);
            return num;

        }
    }
}
